import logging
import os
import threading
import time
import pymysql
from flask import Flask, request, redirect, jsonify
import traceback

from app.models.sys_manage.user import User
from app.utils.logger_util import LoggerUtil
from models.model import get_db
from app.routes.auth.login import auth
from app.utils.mysql_util import MySQLTool
from app.utils.redis_util import RedisTool
from app.utils.scheduler_util import SchedulerUtil
from app.config import Config

"""
create_app 是一个应用工厂函数
"""


def create_app():
    # create and configure the app
    app = Flask(__name__)
    app.config.from_object('app.config.Config')

    # 获取日志记录器
    logger = LoggerUtil.get_logger()
    
    # 添加错误处理
    @app.errorhandler(Exception)
    def handle_exception(e):
        # 记录错误到日志
        logger.error(f"发生错误: {str(e)}", exc_info=True)
        # 如果是在开发环境，可以返回详细错误信息
        if app.debug:
            return jsonify({
                "error": str(e),
                "traceback": traceback.format_exc()
            }), 500
        # 生产环境返回友好的错误信息
        return jsonify({
            "error": "服务器内部错误，请稍后重试"
        }), 500

    # 定义一个全局的拦截器
    @app.before_request
    def before_request():
        #判断请求路径
        path = request.path
        # print(request.path)
        redis_tool = RedisTool.get_instance()
        mysql_tool = MySQLTool.get_instance()
        if path.startswith('/static/'):
            #访问静态资源，直接放行
            return None
        if path not in Config.INTERCEPTOR_WHITELIST:
            #判断是否登录
            cookies = request.cookies
            if cookies is not None:
                #判断是否有token，token是否有效
                token = cookies.get("token")
                if token is not None:
                    #查询用户是否存在
                    user_id = redis_tool.get_value(token)
                    if user_id is None:
                        #登录已过期
                        return redirect('/login?message=登录已过期，请重新登录！')
                    else:
                        #查询用户是否存在
                        users = mysql_tool.query_data(User, user_id=user_id)
                        if users:
                            #用户列表非空，验证成功==》放行
                            #重置token过期时间
                            redis_tool.reset_expiration(token, ex=15 * 60)
                            return None
                else:
                    #用户token信息不完整，重定向到登录页面
                    return redirect('/login?message=请先登录！')
            else:#当前请求未携带cookie
                #重定向到登录页面
                return redirect('/login?message=请先登录！')
    @app.after_request
    def after_request(response):
        pass
        return response

    # 获取db实例，绑定到flask应用
    db = get_db()
    db.init_app(app)

    """
    使用自定义的 LoggerUtil 来统一管理 Flask 应用的日志记录，同时又能较好地兼容 Flask 自身的日志机制，一个比较好的做法是对 Flask 默认的日志记录器进行配置，而不是直接替换它
    """
    # 获取LoggerUtil中的日志记录器
    logger = LoggerUtil.get_logger()
    # 移除Flask默认添加的日志处理器（如果有的话），避免重复记录日志等问题
    app.logger.handlers.clear()
    # 将自定义日志记录器的处理器添加到Flask默认的日志记录器中
    for handler in logger.handlers:
        app.logger.addHandler(handler)

    # 配置Werkzeug日志记录器,确保所有日志信息都通过自定义日志记录器输出
    werkzeug_logger = logging.getLogger('werkzeug')
    werkzeug_logger.setLevel(logging.INFO)
    werkzeug_logger.handlers.clear()
    for handler in logger.handlers:
        werkzeug_logger.addHandler(handler)

    # flask - sqlalchemy在底层尝试使用MySQLdb来连接 MySQL 数据库
    # 添加以下代码来让pymysql模拟MySQLdb
    pymysql.install_as_MySQLdb()

    # 初始化并注册 Redis 工具类插件
    redis_tool = RedisTool.get_instance(app)
    app.extensions['redis_tool'] = redis_tool

    # 初始化并注册 MySQL 工具类插件,MySQLTool是单例模式
    mysql_tool = MySQLTool.get_instance(db)
    app.extensions['mysql_tool'] = mysql_tool

    # # 初始化定时任务调度器实例,SchedulerUtil为单例模式
    task_scheduler = SchedulerUtil.get_instance()

    # 添加定时任务
    task_scheduler.add_job(delete_old_captchas, 'interval', minutes=5)  # 删除过期验证码，每5秒执行一次
    task_scheduler.add_job(delete_expired_logs, 'interval', days=1)  # 删除过期日志文件，每天执行一次

    # 启动一个子线程来运行定时任务调度器
    def run_scheduler():
        try:
            while True:
                time.sleep(1)
        except (KeyboardInterrupt, SystemExit):
            task_scheduler.shutdown()

    scheduler_thread = threading.Thread(target=run_scheduler, name="FlaskSchedulerThread")
    scheduler_thread.daemon = True  # 设置为守护线程,守护线程会随着主线程的结束而自动结束
    scheduler_thread.start()

    # 注册认证蓝图
    app.register_blueprint(auth)
    logger.info(f"flask实例启动成功...")
    return app


def delete_old_captchas():
    """
    删除指定路径下创建时间距离当前时间超过5分钟的图片
    """
    target_path = "./static/tmp"  # 替换为实际的目标路径
    logger = LoggerUtil.get_logger()
    current_time = time.time()
    five_minutes_ago = current_time - 5 * 60  # 计算5分钟前的时间戳
    # print(os.walk(target_path))
    for root, dirs, files in os.walk(target_path):
        # print(root, dirs, files)
        for file in files:
            # print(file)
            file_extension = os.path.splitext(file)[1].lower()  # 获取文件后缀名并转为小写
            if file_extension in ['.jpg', '.png', '.jpeg']:  # 判断是否是图片文件
                file_path = os.path.join(root, file)
                file_creation_time = os.path.getctime(file_path)
                if file_creation_time < five_minutes_ago:
                    try:
                        os.remove(file_path)
                        logger.info(f"已成功删除图片: {file_path}")
                    except OSError as e:
                        logger.error(f"删除图片 {file_path} 时出错: {e}")


def delete_expired_logs():
    """
    删除30天前的过期日志文件
    """
    logger = LoggerUtil.get_logger()
    current_time = time.time()
    log_dir = "./utils/logs"
    # 时间阈值，30天前的时间戳
    # threshold = current_time - 1 * 24 * 60 * 60  # 开发环境 删除1天前的日志文件
    threshold = current_time - 30 * 24 * 60 * 60
    for file_name in os.listdir(log_dir):
        file_path = os.path.join(log_dir, file_name)
        if os.path.isfile(file_path):
            file_time = os.path.getctime(file_path)
            if file_time < threshold:
                try:
                    os.remove(file_path)
                    logger.info(f"已删除过期日志文件: {file_path}")
                except OSError as e:
                    logger.error(f"删除过期日志文件 {file_path} 时出错: {str(e)}")


if __name__ == "__main__":
    # 在Flask开发环境中，出现日志记录器被重复创建的现象通常是因为Flask的开发服务器会自动重载应用程序。这种重载机制会导致应用程序被多次启动，从而导致单例模式的对象（如日志记录器）被多次创建
    # 检查Flask的自动重载：
    # 在开发环境中，Flask的自动重载功能会在检测到代码更改时重新启动应用程序。这会导致单例对象被重新创建。
    # 你可以通过设置FLASK_ENV = production来禁用自动重载功能，或者在启动Flask应用时使用 - -no - reload选项。
    create_app().run(debug=True, use_reloader=False)
